package com.mgq.juc.c1;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * @author MaGuangQi
 *
 * @date 2021-12-24 14:56
 **/
public class TestConcurrentHashMap {

    public static void main(String[] args) {
        demo(
                // 创建map 集合 能否使用HashMap ?
               // (Supplier<Map<String, Integer>>) HashMap::new,
                // 使用ConcurrentHashMap能否解决? 不能. 原因是for循环中涉及到多个操作.单个操作是线程安全,组合后就不是了
                (Supplier<Map<String, LongAdder>>) ConcurrentHashMap::new,
                (map,words)->{
                    for (String word : words) {
                        //如果缺少一个key,则计算生成一个value,将key,value放入map中. 是一个原子操作
                        //相当于是下面2步
                        /*if (map.get(word) == null) {
                            map.put(word,1);
                        }*/
                        LongAdder adder = map.computeIfAbsent(word, (key) -> new LongAdder());
                        adder.increment();
                        //错误解决方法, 不建议使用synchronized, 因为ConcurrentHashMap本身就是无锁的
                        //使用了synchronized又是重量级锁了
                        /*synchronized (map) {
                            Integer count = map.get(word);
                            int newValue = count == null ? 1 : count + 1;
                            map.put(word, newValue);
                        }*/

                    }
                }

        );
    }

    /**
     *
     * @param supplier 需要提供map
     * @param consumer consumer逻辑(Map<String, V>, List<String>)
     * @param <V> 泛型
     */
    public static <V> void demo(Supplier<Map<String, V>> supplier, BiConsumer<Map<String, V>, List<String>> consumer) {
        Map<String, V> map = supplier.get();
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 26; i++) {
            int finalI = i;
            Thread thread = new Thread(() -> {
                List<String> words = readFromFile(finalI);
                consumer.accept(map, words);
            });
            list.add(thread);
        }
        list.forEach(Thread::start);
        list.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println(map);
    }

    public static List<String> readFromFile(int i) {
        try (BufferedReader reader = new BufferedReader(new FileReader(new File("tmp/" + (i + 1) + ".txt")))) {
            String s = "";
            List<String> list=new ArrayList<>();
            while ((s = reader.readLine()) != null) {
                list.add(s);
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }
}
